home *** CD-ROM | disk | FTP | other *** search
/ CD ROM Paradise Collection 4 / CD ROM Paradise Collection 4 1995 Nov.iso / program / swags_z.zip / SOUND.SWG / 0021_Play CMF Files on SB.pas < prev    next >
Pascal/Delphi Source File  |  1993-07-16  |  13KB  |  451 lines

  1. UNIT CMFTool;
  2. {** Unit - uses SBFMDRV.COM **}
  3. INTERFACE
  4. USES Dos;
  5. TYPE
  6.   CMFFileTyp = FILE;
  7.   CMFDataTyp = Pointer;
  8.   CMFHeader = RECORD
  9.     CMFFileID         : ARRAY[0..3] OF CHAR;
  10.     CMFVersion        : WORD;
  11.     CMFInstrBlockOfs  : WORD;
  12.     CMFMusicBlockOfs  : WORD;
  13.     CMFTickPerBeat    : WORD;
  14.     CMFClockTicksPS   : WORD;
  15.     CMFFileTitleOfs   : WORD;
  16.     CMFComposerOfs    : WORD;
  17.     CMFMusicRemarkOfs : WORD;
  18.     CMFChannelsUsed   : ARRAY[0..15] OF CHAR;
  19.     CMFInstrNumber    : WORD;
  20.     CMFBasicTempo     : WORD;
  21.   END;
  22. CONST
  23.    CMFToolVersion       = 'v1.0';
  24. VAR
  25.    CMFStatusByte      : BYTE;
  26.    CMFErrStat         : WORD;
  27.    CMFDriverInstalled : BOOLEAN;
  28.    CMFDriverIRQ       : WORD;
  29.    CMFSongPaused      : BOOLEAN;
  30.    OldExitProc        : Pointer;
  31. PROCEDURE PrintCMFErrMessage;
  32. FUNCTION  CMFGetSongBuffer(VAR CMFBuffer : Pointer; CMFFile : STRING):BOOLEAN;
  33. FUNCTION  CMFFreeSongBuffer (VAR CMFBuffer : Pointer):BOOLEAN;
  34. FUNCTION  CMFInitDriver : BOOLEAN;
  35. FUNCTION  CMFGetVersion : WORD;
  36. PROCEDURE CMFSetStatusByte;
  37. FUNCTION  CMFSetInstruments(VAR CMFBuffer : Pointer):BOOLEAN;
  38. FUNCTION  CMFSetSingleInstruments(VAR CMFInstrument:Pointer; No:WORD):BOOLEAN;
  39. PROCEDURE CMFSetSysClock(Frequency : WORD);
  40. PROCEDURE CMFSetDriverClock(Frequency : WORD);
  41. PROCEDURE CMFSetTransposeOfs (Offset : INTEGER);
  42. FUNCTION  CMFPlaySong(VAR CMFBuffer : Pointer) : BOOLEAN;
  43. FUNCTION  CMFStopSong : BOOLEAN;
  44. FUNCTION  CMFResetDriver:BOOLEAN;
  45. FUNCTION  CMFPauseSong : BOOLEAN;
  46. FUNCTION  CMFContinueSong : BOOLEAN;
  47. IMPLEMENTATION
  48. TYPE
  49.    TypeCastTyp = ARRAY [0..6000] of Char;
  50. VAR
  51.    Regs : Registers;
  52.    CMFIntern : ^CMFHeader; { Internal pointer to CMF structure }
  53. PROCEDURE PrintCMFErrMessage;
  54. { PURPOSE : Displays SB error as text; no change to error status. }
  55. BEGIN
  56.    CASE CMFErrStat OF
  57.       100 : Write(' SBFMDRV sound driver not found ');
  58.       110 : Write(' Driver reset successful ');
  59.       200 : Write(' CMF file not found ');
  60.       210 : Write(' No memory free for CMF file ');
  61.       220 : Write(' File not in CMF format ');
  62.       300 : Write(' Memory allocation error occurred ');
  63.       400 : Write(' Too many instruments defined ');
  64.       500 : Write(' CMF data could not be played ');
  65.       510 : Write(' CMF data could not be stopped ');
  66.       520 : Write(' CMF data could not be paused ');
  67.       530 : Write(' CMF data could not be continued ');
  68.       END;
  69.    END;
  70. FUNCTION Exists (Filename : STRING):BOOLEAN;
  71. { PURPOSE : Checks for the existence of a file, and returns a Boolean exp. }
  72. VAR
  73.    F : File;
  74. BEGIN
  75.    Assign(F,Filename);
  76. {$I-}
  77.    Reset(F);
  78.    Close(F);
  79. {$I+}
  80.    Exists := (IoResult = 0) AND (Filename <> '');
  81.    END;
  82. PROCEDURE AllocateMem (VAR Pt : Pointer; Size : LongInt);
  83. { Reserves as many bytes as Size allows, then sets the pointer in the
  84.   Pt variable. If not enough memory is available, Pt is set to NIL. }
  85. VAR
  86.    SizeIntern : WORD;
  87. BEGIN
  88.    Inc(Size,15);
  89.    SizeIntern := (Size shr 4);
  90.    Regs.AH := $48;
  91.    Regs.BX := SizeIntern;
  92.    MsDos(Regs);
  93.    IF (Regs.BX <> SizeIntern) THEN Pt := NIL
  94.    ELSE Pt := Ptr(Regs.AX,0);
  95.    END;
  96. FUNCTION  CheckFreeMem (VAR CMFBuffer : Pointer; CMFSize : LongInt):BOOLEAN;
  97. { Ensures that enough memory has been allocated for CMF file. }
  98. BEGIN
  99.    AllocateMem(CMFBuffer,CMFSize);
  100.    CheckFreeMem := CMFBuffer <> NIL;
  101.    END;
  102. FUNCTION  CMFGetSongBuffer(VAR CMFBuffer : Pointer; CMFFile : STRING):BOOLEAN;
  103. { Loads file into memory; returns TRUE if load successful, FALSE if not. }
  104. CONST
  105.    FileCheck : STRING[4] = 'CTMF';
  106. VAR
  107.    CMFFileSize : LongInt;
  108.    FPresent    : BOOLEAN;
  109.    VFile       : CMFFileTyp;
  110.    Segs        : WORD;
  111.    Read        : WORD;
  112.    Checkcount  : BYTE;
  113. BEGIN
  114.    FPresent := Exists(CMFFile);
  115.  
  116. { CMF file could not be found }
  117.    IF Not(FPresent) THEN BEGIN
  118.       CMFGetSongBuffer := FALSE;
  119.       CMFErrStat   := 200;
  120.       EXIT
  121.       END;
  122.    Assign(VFile,CMFFile);
  123.    Reset(VFile,1);
  124.    CMFFileSize := Filesize(VFile);
  125.    AllocateMem(CMFBuffer,CMFFileSize);
  126. { Insufficient memory for CMF file }
  127.    IF (CMFBuffer = NIL) THEN BEGIN
  128.       Close(VFile);
  129.       CMFGetSongBuffer := FALSE;
  130.       CMFErrStat   := 210;
  131.       EXIT;
  132.       END;
  133.    Segs := 0;
  134.    REPEAT
  135.       Blockread(VFile,Ptr(seg(CMFBuffer^)+4096*Segs,Ofs(CMFBuffer^))^,$FFFF,Read
  136. );
  137.       Inc(Segs);
  138.       UNTIL Read = 0;
  139.    Close(VFile);
  140. { File not in CMF format }
  141.    CMFIntern := CMFBuffer;
  142.    CheckCount := 1;
  143.    REPEAT
  144.       IF FileCheck[CheckCount] = CMFIntern^.CMFFileID[CheckCount-1]
  145.          THEN Inc(CheckCount)
  146.          ELSE CheckCount := $FF;
  147.       UNTIL CheckCount >= 3;
  148.    IF NOT(CheckCount = 3) THEN BEGIN
  149.       CMFGetSongBuffer := FALSE;
  150.       CMFErrStat   := 220;
  151.       EXIT;
  152.       END;
  153. { Load was successful }
  154.    CMFGetSongBuffer := TRUE;
  155.    CMFErrStat   := 0;
  156.    END;
  157. FUNCTION CMFFreeSongBuffer (VAR CMFBuffer : Pointer):BOOLEAN;
  158. { Frees memory allocated for CMF file. }
  159. BEGIN
  160.    Regs.AH := $49;
  161.    Regs.ES := seg(CMFBuffer^);
  162.    MsDos(Regs);
  163.    CMFFreeSongBuffer := TRUE;
  164.    IF (Regs.AX = 7) OR (Regs.AX = 9) THEN BEGIN
  165.       CMFFreeSongBuffer := FALSE;
  166.       CMFErrStat := 300
  167.       END;
  168.    END;
  169. FUNCTION CMFInitDriver : BOOLEAN;
  170. { Checks for SBFMDRV.COM resident in memory, and resets driver }
  171. CONST
  172.    DriverCheck :STRING[5] = 'FMDRV';
  173. VAR
  174.    ScanIRQ,
  175.    CheckCount  : BYTE;
  176.    IRQPtr,
  177.    DummyPtr    : Pointer;
  178.  
  179. BEGIN
  180. { Possible SBFMDRV interrupts lie in range $80 - $BF }
  181.    FOR ScanIRQ := $80 TO $BF DO BEGIN
  182.       GetIntVec(ScanIRQ, IRQPtr);
  183.       DummyPtr := Ptr(Seg(IRQPtr^), $102);
  184. { Check for string 'FMDRV' in interrupt program. }
  185.       CheckCount := 1;
  186.       REPEAT
  187.          IF DriverCheck[CheckCount] = TypeCastTyp(DummyPtr^)[CheckCount]
  188.             THEN Inc(CheckCount)
  189.             ELSE CheckCount := $FF;
  190.          UNTIL CheckCount >= 5;
  191.       IF (CheckCount = 5) THEN BEGIN
  192. { String found; reset executed }
  193.          Regs.BX := 08;
  194.          CMFDriverIRQ := ScanIRQ;
  195.          Intr(CMFDriverIRQ, Regs);
  196.          IF Regs.AX = 0 THEN
  197.             CMFInitDriver := TRUE
  198.          ELSE BEGIN
  199.             CMFInitDriver := FALSE;
  200.             CMFErrStat    := 110;
  201.             END;
  202.          Exit;
  203.          END
  204.       ELSE BEGIN
  205. { String not found }
  206.          CMFInitDriver := FALSE;
  207.          CMFErrStat := 100;
  208.          END;
  209.       END;
  210.    END;
  211. FUNCTION CMFGetVersion : WORD;
  212. { Gets version number from SBFMDRV driver. }
  213. BEGIN
  214.    Regs.BX := 0;
  215.    Intr(CMFDriverIRQ,Regs);
  216.    CMFGetVersion := Regs.AX;
  217.    END;
  218. PROCEDURE CMFSetStatusByte;
  219. { Place driver status byte in CMFStatusByte variable. }
  220. BEGIN
  221.    Regs.BX:= 1;
  222.    Regs.DX:= Seg(CMFStatusByte);
  223.    Regs.AX:= Ofs(CMFStatusByte);
  224.    Intr(CMFDriverIRQ, Regs);
  225.    END;
  226. FUNCTION CMFSetInstruments(VAR CMFBuffer : Pointer):BOOLEAN;
  227. { Sets SB card FM registers to instrumentation stated in CMF file. }
  228. BEGIN
  229.     CMFIntern := CMFBuffer;
  230.     IF CMFIntern^.CMFInstrNumber > 128 THEN BEGIN
  231.        CMFErrStat := 400;
  232.        CMFSetInstruments := FALSE;
  233.        Exit;
  234.        END;
  235.     Regs.BX := 02;
  236.     Regs.CX := CMFIntern^.CMFInstrNumber;
  237.     Regs.DX := Seg(CMFBuffer^);
  238.     Regs.AX := Ofs(CMFBuffer^)+CMFIntern^.CMFInstrBlockOfs;
  239.     Intr(CMFDriverIRQ, Regs);
  240.     CMFSetInstruments := TRUE;
  241.    END;
  242. FUNCTION CMFSetSingleInstruments(VAR CMFInstrument:Pointer; No:WORD):BOOLEAN;
  243. { Sets SB FM registers to instrument values corresponding to the
  244.   data structure following the CMFInstrument pointer. }
  245. BEGIN
  246.     IF No > 128 THEN BEGIN
  247.        CMFErrStat := 400;
  248.        CMFSetSingleInstruments := FALSE;
  249.        Exit;
  250.        END;
  251.     Regs.BX := 02;
  252.     Regs.CX := No;
  253.     Regs.DX := Seg(CMFInstrument^);
  254.     Regs.AX := Ofs(CMFInstrument^);
  255.     Intr(CMFDriverIRQ, Regs);
  256.     CMFSetSingleInstruments := TRUE;
  257.    END;
  258. PROCEDURE CMFSetSysClock(Frequency : WORD);
  259. { Sets default value of timer 0 to new value. }
  260. BEGIN
  261.    Regs.BX := 03;
  262.    Regs.AX := (1193180 DIV Frequency);
  263.    Intr(CMFDriverIRQ, Regs);
  264.    END;
  265. PROCEDURE CMFSetDriverClock(Frequency : WORD);
  266. { Sets driver timer frequency to new value. }
  267.  
  268. BEGIN
  269.    Regs.BX := 04;
  270.    Regs.AX := (1193180 DIV Frequency);
  271.    Intr(CMFDriverIRQ, Regs);
  272.    END;
  273. PROCEDURE CMFSetTransposeOfs (Offset : INTEGER);
  274. { Transposes all notes in the CMF file by "Offset." }
  275. BEGIN
  276.    Regs.BX := 05;
  277.    Regs.AX := Offset;
  278.    Intr(CMFDriverIRQ, Regs);
  279.    END;
  280. FUNCTION CMFPlaySong(VAR CMFBuffer : Pointer) : BOOLEAN;
  281. { Initializes all important parameters and starts song playback. }
  282. VAR
  283.    Check : BOOLEAN;
  284. BEGIN
  285.    CMFIntern := CMFBuffer;
  286. { Set driver clock frequency }
  287.    CMFSetDriverClock(CMFIntern^.CMFClockTicksPS);
  288. { Set instruments }
  289.    Check := CMFSetInstruments(CMFBuffer);
  290.    IF Not(Check) THEN Exit;
  291.    Regs.BX := 06;
  292.    Regs.DX := Seg(CMFIntern^);
  293.    Regs.AX := Ofs(CMFIntern^)+CMFIntern^.CMFMusicBlockOfs;
  294.    Intr(CMFDriverIRQ, Regs);
  295.    IF Regs.AX = 0 THEN BEGIN
  296.       CMFPlaySong := TRUE;
  297.       CMFSongPaused := FALSE;
  298.       END
  299.    ELSE BEGIN
  300.       CMFPlaySong := FALSE;
  301.       CMFErrStat := 500;
  302.       END;
  303.    END;
  304. FUNCTION CMFStopSong : BOOLEAN;
  305. { Attempts to stop song playback. }
  306. BEGIN
  307.    Regs.BX := 07;
  308.    Intr(CMFDriverIRQ, Regs);
  309.    IF Regs.AX = 0 THEN
  310.       CMFStopSong := TRUE
  311.    ELSE BEGIN
  312.       CMFStopSong := FALSE;
  313.       CMFErrStat  := 510;
  314.       END;
  315.    END;
  316. FUNCTION CMFResetDriver:BOOLEAN;
  317. { Resets driver to starting status. }
  318. BEGIN
  319.    Regs.BX := 08;
  320.    Intr(CMFDriverIRQ, Regs);
  321.    IF Regs.AX = 0 THEN
  322.       CMFResetDriver := TRUE
  323.    ELSE BEGIN
  324.       CMFResetDriver := FALSE;
  325.       CMFErrStat    := 110;
  326.       END;
  327.    END;
  328. FUNCTION CMFPauseSong : BOOLEAN;
  329. { Attempts to pause song playback. If pause is possible, this
  330.   function sets the CMFSongPaused variable to TRUE. }
  331. BEGIN
  332.    Regs.BX := 09;
  333.    Intr(CMFDriverIRQ, Regs);
  334.    IF Regs.AX = 0 THEN BEGIN
  335.       CMFPauseSong  := TRUE;
  336.       CMFSongPaused := TRUE;
  337.       END
  338.    ELSE BEGIN
  339.       CMFPauseSong := FALSE;
  340.       CMFErrStat   := 520;
  341.       END;
  342.    END;
  343. FUNCTION CMFContinueSong : BOOLEAN;
  344. { Attempts to continue playback of a paused song. If continuation
  345.   is possible, this function sets CMFSongPaused to FALSE. }
  346. BEGIN
  347.    Regs.BX := 10;
  348.    Intr(CMFDriverIRQ, Regs);
  349.    IF Regs.AX = 0 THEN BEGIN
  350.       CMFContinueSong  := TRUE;
  351.       CMFSongPaused    := FALSE;
  352.       END
  353.    ELSE BEGIN
  354.       CMFContinueSong := FALSE;
  355.       CMFErrStat      := 530;
  356.  
  357.       END;
  358.    END;
  359. {$F+}
  360. PROCEDURE CMFToolsExitProc;
  361. {$F-}
  362. { Resets the status byte address, allowing this program to exit.}
  363. BEGIN
  364.    Regs.BX:= 1;
  365.    Regs.DX:= 0;
  366.    Regs.AX:= 0;
  367.    Intr(CMFDriverIRQ, Regs);
  368.    ExitProc := OldExitProc;
  369.    END;
  370. BEGIN
  371. { Reset old ExitProc to the Tool unit proc }
  372.    OldExitProc := ExitProc;
  373.    ExitProc := @CMFToolsExitProc;
  374. { Initialize variables }
  375.    CMFErrStat := 0;
  376.    CMFSongPaused := FALSE;
  377. { Initialize driver }
  378.    CMFDriverInstalled := CMFInitDriver;
  379.    IF CMFDriverInstalled THEN BEGIN
  380.       CMFStatusByte := 0;
  381.       CMFSetStatusByte;
  382.       END;
  383.    END.
  384.  
  385. { ---------------------    DEMO PROGRAM  -----------------  }
  386.  
  387. Program CMFDemo;
  388. {* Demo program for CMFTOOL unit *}
  389. {$M 16384,0,65535}
  390. Uses CMFTool,Crt;
  391. VAR
  392.    Check      : BOOLEAN;
  393.    SongName   : String;
  394.    SongBuffer : CMFDataTyp;
  395. PROCEDURE TextNumError;
  396. {* INPUT   : None; data comes from CMFErrStat global variable
  397.  * OUTPUT  : None
  398.  * PURPOSE : Displays SB error as text, including error number. }
  399. BEGIN
  400.    Write(' Error #',CMFErrStat:3,': ');
  401.    PrintCMFErrMessage;
  402.    WriteLn;
  403.    Halt(CMFErrStat);
  404.    END;
  405. BEGIN
  406.    ClrScr;
  407. { Displays error if SBFMDRV driver has not been installed }
  408.    IF Not (CMFDriverInstalled) THEN TextNumError;
  409. { If no song name is included with command line parameters,
  410.   program searches for the default name (here STARFM.CMF). }
  411.    IF ParamCount = 0 THEN SongName := 'STARFM.CMF'
  412.                      ELSE SongName := ParamStr(1);
  413. { Display driver's version and subversion numbers }
  414.    GotoXY(28,5);
  415.    Write  ('SBFMDRV Version ',Hi(CMFGetVersion):2,'.');
  416.    WriteLn(Lo(CMFGetVersion):2,' loaded');
  417. { Display interrupt number in use }
  418.    GotoXY(24,10);
  419.    Write  ('System interrupt (IRQ) ');
  420.    WriteLn(CMFDriverIRQ:3,' in use');
  421.    GotoXY(35,15);
  422.    WriteLn('Song Status');
  423.    GotoXY(31,23);
  424.    WriteLn('Song name: ',SongName);
  425. { Load song file }
  426.    Check := CMFGetSongBuffer(SongBuffer,SongName);
  427.    IF NOT(Check) THEN TextNumError;
  428. { CMFSetTransposeOfs() controls transposition down or up of the loaded song
  429.   (positive values transpose up, negative values transpose down). The value
  430.   0 plays the loaded song in its original key. }
  431.    CMFSetTransposeOfs(0); { Experiment with this value }
  432. { Play song }
  433.    Check := CMFPlaySong(SongBuffer);
  434.    IF NOT(Check) THEN TextNumError;
  435. { During playback, display status byte }
  436.    REPEAT
  437.       GotoXY(41,17);Write(CMFStatusByte:3);
  438.       UNTIL (KeyPressed OR (CMFStatusByte = 0));
  439. { Stop playback if user presses a key }
  440.    IF KeyPressed THEN BEGIN
  441.       Check := CMFStopSong;
  442.       IF NOT(Check) THEN TextNumError;
  443.       END;
  444. { Re-initialize driver }
  445.    Check := CMFResetDriver;
  446.    IF NOT(Check) THEN TextNumError;
  447. { Free song file memory }
  448.    Check := CMFFreeSongBuffer(SongBuffer);
  449.    IF NOT(Check) THEN TextNumError;
  450.    END.
  451.